개요
쿠버네티스로 Pinterest 기반의 웹 서비스를 배포해보려고 한다.
여기서 Pod 간 NFS 연동, PV-PVC, ConfigMap 등을 종합적으로 사용해볼 것이다.
프로젝트 구조
Django 개발 서버 디렉토리 구조
├── apps
│ ├── accountapp
│ ├── articleapp
│ ├── commentapp
│ ├── dislikeapp
│ ├── Dockerfile
│ ├── likeapp
│ ├── manage.py
│ ├── media
│ ├── pragmatic
│ ├── profileapp
│ ├── projectapp
│ ├── requirements.txt
│ ├── static
│ ├── staticfiles
│ ├── subscribeapp
│ └── templates
├── configMap
│ └── django-config.yaml
├── k8s-db-deployment.yaml
├── k8s-django-deployment.yaml
├── k8s-nginx-deployment.yaml
└── pv-pvc
├── db-pv-pvc.yaml
├── nginx-media-pv-pvc.yaml
├── nginx-static-pv-pvc.yaml
├── web-media-pv-pvc.yaml
└── web-static-pv-pvc.yaml
사전 준비
- 외부로 배포하기 위해 Nginx Ingress Controller & metalLB 설치 필요
- 관련 작업 링크
postgresql DB 접속을 위해 django의 settings.py
을 아래와 같이 수정
...
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('POSTGRES_DB','postgres'),
'USER': os.getenv('POSTGRES_USER','postgres'),
'PASSWORD': os.getenv('POSTGRES_PASSWORD','postgres'),
'HOST': os.getenv('POSTGRES_HOST','postgres-service'),
'PORT': int(os.getenv('POSTGRES_PORT',5432))
}
}
...
프로젝트 생성
- 해당 섹션의 하위 작업들은 모두 배포 서버(k8s)가 아닌 개발 서버에서 진행
Django 프로젝트 생성
# 가상환경 활성화
pyenv virtualenv 3.11.6 py3_11_6
pyenv activate py3_11_6
# Django 설치
pip install django
# 장고 프로젝트 생성
django-admin startproject myapp .
도커 이미지 생성 & 빌드
- 개발한 Django 앱을 Dockerhub에 배포
- postgresql은 빌드된 이미지 없이 순정 이미지를 pull 받아서 띄울 것임.
django
Dockerfile 작성
# pull official base image
FROM python:3.8-slim-buster
# set work directory
WORKDIR /usr/src/app
# set environment variable
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONBUFFERED 1
COPY . .
# install dependencies
RUN pip install --upgrade pip && pip install -r requirements.txt
RUN python manage.py migrate
CMD ["gunicorn", "pragmatic.wsgi:application", "--bind", "0.0.0.0:8000", "--reload"]
이미지 배포
- 이미지 registry 서버를 따로 구축하여 해당 서버로부터 이미지를
pull
받아오도록 하였음.
이미지 빌드
docker build --tag xxxx/k8s-pinterest .
레지스트리 서버로 이미지 배포 위해 이미지 태그 변경
docker tag xxxx/k8s-pinterest <registry_서버_IP>:5000/k8s-pinterest
레지스트리 서버로 이미지 배포
docker push <registry_서버_IP>:5000/k8s-pinterest
서비스 배포하기
- 해당 섹션의 하위 작업들은 쿠버네티스 마스터 노드에서 진행
PV 및 PVC 생성
Django, PostgreSQL, nginx 데이터를 위한 스토리지를 설정하자.
이 때 PV는 NFS로 설정한다.
참고로 nginx의 PV-PVC
를 생성하는 이유는 다음과 같다.
- Django 앱의
staticfiles
,media
에 대한 파일 서빙이 필요함.
코드를 작성해보자.
DB용: db-pv-pvc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: db-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
nfs:
server: 192.168.219.179
path: /nfs/share/db
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
APP용: app-pv-pvc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web-static-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
nfs:
server: 192.168.219.179
path: /nfs/share/web/staticfiles
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-static-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: web-media-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
nfs:
server: 192.168.219.179
path: /nfs/share/web/media
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-media-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
ConfigMap 생성
Django와 PostgreSQL의 환경변수를 설정하자.
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: django-config
data:
DEBUG: "1"
SECRET_KEY: "XX"
DJANGO_ALLOWED_HOSTS: "localhost 127.0.0.1 192.168.219.0/24 [::1]"
SQL_ENGINE: "django.db.backends.postgresql"
SQL_DATABASE: "pragmatic"
SQL_USER: "pragmatic_user"
SQL_PASSWORD: "pragmatic_pass"
SQL_HOST: "db"
SQL_PORT: "5432"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config
data:
POSTGRES_USER: "pragmatic_user"
POSTGRES_PASSWORD: "pragmatic_pass"
POSTGRES_DB: "pragmatic"
PostgreSQL Deployment 및 Service 생성
PostgreSQL 데이터베이스 설정이다.
k8s-db-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:12.0-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
valueFrom:
configMapKeyRef:
name: postgres-config
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
configMapKeyRef:
name: postgres-config
key: POSTGRES_PASSWORD
- name: POSTGRES_DB
valueFrom:
configMapKeyRef:
name: postgres-config
key: POSTGRES_DB
volumeMounts:
- name: db-data
mountPath: /var/lib/postgresql/data
volumes: # This section must align properly under 'spec'
- name: db-data
persistentVolumeClaim:
claimName: db-pvc
---
apiVersion: v1
kind: Service
metadata:
name: db
spec:
selector:
app: postgres
ports:
- protocol: TCP
port: 5432
targetPort: 5432
clusterIP: None # StatefulSets와 비슷하게 Pod 간 직접 통신 지원
Django Deployment 및 Service 생성
Django 애플리케이션 설정이다.
k8s-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: django
spec:
replicas: 2
selector:
matchLabels:
app: django
template:
metadata:
labels:
app: django
spec:
containers:
- name: django
image: yshrim12/pinterest
ports:
- containerPort: 8000
env:
- name: DEBUG
valueFrom:
configMapKeyRef:
name: django-config
key: DEBUG
- name: SECRET_KEY
valueFrom:
configMapKeyRef:
name: django-config
key: SECRET_KEY
- name: DJANGO_ALLOWED_HOSTS
valueFrom:
configMapKeyRef:
name: django-config
key: DJANGO_ALLOWED_HOSTS
- name: SQL_ENGINE
valueFrom:
configMapKeyRef:
name: django-config
key: SQL_ENGINE
- name: SQL_DATABASE
valueFrom:
configMapKeyRef:
name: django-config
key: SQL_DATABASE
- name: SQL_USER
valueFrom:
configMapKeyRef:
name: django-config
key: SQL_USER
- name: SQL_PASSWORD
valueFrom:
configMapKeyRef:
name: django-config
key: SQL_PASSWORD
- name: SQL_HOST
valueFrom:
configMapKeyRef:
name: django-config
key: SQL_HOST
- name: SQL_PORT
valueFrom:
configMapKeyRef:
name: django-config
key: SQL_PORT
volumeMounts:
- name: static-data
mountPath: /usr/src/app/staticfiles
- name: media-data
mountPath: /usr/src/app/media
volumes:
- name: static-data
persistentVolumeClaim:
claimName: web-static-pvc
- name: media-data
persistentVolumeClaim:
claimName: web-media-pvc
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: django
ports:
- protocol: TCP
port: 8000
targetPort: 8000
Nginx Deployment 및 Service 생성
Nginx 설정이다.
k8s-nginx-deployment.yaml
# nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf
- name: static-data
mountPath: /usr/src/app/staticfiles
- name: media-data
mountPath: /usr/src/app/media
volumes:
- name: nginx-config
configMap:
name: nginx-config
- name: static-data
persistentVolumeClaim:
claimName: web-static-pvc
- name: media-data
persistentVolumeClaim:
claimName: web-media-pvc
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
nginx.conf: |
upstream pragmatic {
server web:8000;
}
server {
listen 80;
client_max_body_size 0;
location / {
proxy_pass http://pragmatic;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /static/ {
alias /usr/src/app/staticfiles/;
}
location /media/ {
alias /usr/src/app/media/;
}
}
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30001
type: NodePort
nginx에서도 web-static-pvc
와 web-media-pvc
를 사용하도록 하자.
data:
nginx.conf: |
upstream pragmatic {
server web:8000;
}
server {
listen 80;
client_max_body_size 0;
location / {
proxy_pass http://pragmatic;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /static/ {
alias /usr/src/app/staticfiles/;
}
location /media/ {
alias /usr/src/app/media/;
}
}
---
nginx에서 django 앱의 /static/
, /media/
경로에 대해 정적 파일 서빙을 해주기 위해서는
django 파드 내에 있는 /usr/src/app/staticfiles
, /usr/src/app/media
경로에 파일들이 필요한데
이때 nginx에서도 web-static-pvc
와 web-media-pvc
를 사용하여
NFS 서버를 통해 해당 파일들을 nginx 파드로 넘겨줄 수 있도록 하는거다.
그리고 data.nginx.conf
의 설정 값 중에,
upstream 섹션의 server web:8000;
이 부분은 django 앱의 서비스 명세서 이름을 지정한 것이다.
이는 쿠버네티스의 서비스 디스커버리(Service Discovery)에 대해 알고 있다면 무슨 뜻인지 이해가 갈 것이다.
리소스 적용
모든 YAML 파일을 클러스터에 적용하자.
# ConfigMap 적용
kubectl apply -f ./configmap
# pv-pvc 적용
kubectl apply -f ./pv-pvc
# deployment 적용
kubectl apply -f .
여기서 ./configmap
은 configmap이 모여있는 경로이고,
./pv-pvc
는 pv-pvc 명세서가 모여있는 경로이다.
접속 테스트
인그레스(Ingress) 적용
Ingress 실습 & helm으로 ingress-nginx-controller 설치하기 참고하기
트러블슈팅
watch
명령으로 Pod 간 STATUS
에 이상이 없는지 모니터링
watch kubectl get pods -o wide
Pod 로그/이벤트 조회
kubectl describe pod/<pod_name>
413 Request entity too large
nginx-ingress
에서 413 Request entity too large
에러 발생.
413 Request entity too large 해결하기 - k8s/ingress